<template>
  <div class="hello">
    <highcharts :options="chartOptions"></highcharts>
    <div class="heatmap" :style="getHeight" ref="heatmap"><span>瀑布图(dBm)</span></div>
  </div>
</template>

<script>
import {Chart} from 'highcharts-vue'
import Highcharts from 'highcharts'
import exportingInit from 'highcharts/modules/exporting'
exportingInit(Highcharts)


import {round} from 'lodash'

let colormap = require('colormap')
export default {
  name: 'HelloWorld',
  components: {
    highcharts: Chart
  },
  data() {
    return {
      dom: {},
      waterFallDom: {},
      canvas: {},
      ctx: {},
      waterFallCtx: {},
      legend: {},
      colormap: [],
      canvasSize: 0,
      spectrumHeight: 0,
      cutStart: 0,
      cutEnd: 0,
      coordinatePercentage: [],
      randomArray: [],



      chartOptions: {
        chart: {
          zoomType: 'x'
        },
        boost: {
          useGPUTranslations: true
        },
        // title: {
        //   text: 'Highcharts drawing ' + n + ' points'
        // },
        subtitle: {
          text: 'Using the Boost module'
        },
        tooltip: {
          valueDecimals: 2
        },
        series: [{
          data: [],
          lineWidth: 0.5
        }]
      }
    }
  },
  props: {
    padding: {type: Array, default: () => [0, 2, 2, 64]},
    colormapName: {type: String, default: 'jet'},
    canvasRows: {type: Number, default: 250},
    minDb: {type: Number, default: -125},
    maxDb: {type: Number, default: 0}
  },
  async mounted() {
    const checkDom = setInterval(() => {
      if (this.$refs.heatmap !== undefined) {
        clearInterval(checkDom)
        this.setColormap()
        this.createCanvas()
        this.drawLegend()
        this.$emit('onload')
        setInterval(() => {
          this.randomArray = []
          // 随机数，这里可以更具你的需求修改的数据量，我默认了3w个点
          this.randomArray = Array.from({length: 30000}, () => -Math.floor(Math.random() * 100) + 1);
          this.highchartData = this.getData(this.randomArray);
          this.chartOptions.series[0].data = this.highchartData
          this.addData(this.randomArray)
        }, 1000)
      }
    }, 30)
  },
  methods: {
    getData(n) {
      var arr = []
      for (let i = 0; i < n.length; i = i + 1) {
        // if (i % 100 === 0) {
        //   a = 2 * Math.random();
        // }
        // if (i % 1000 === 0) {
        //   b = 2 * Math.random();
        // }
        // if (i % 10000 === 0) {
        //   c = 2 * Math.random();
        // }
        // if (i % 50000 === 0) {
        //   spike = 10;
        // } else {
        //   spike = 0;
        // }
        arr.push([
          i,
          2 * Math.sin(i / 100) + Math.random()
        ]);
      }
      return arr;
    },






    createCanvas() {
      this.dom = this.$refs.heatmap
      this.dom.addEventListener('click', this.bindingClick)
      this.canvas = document.createElement('canvas')
      this.setWidth()
      this.dom.appendChild(this.canvas)
      this.ctx = this.canvas.getContext('2d')
      // 创建瀑布图(隐藏状态，用于保存已生成的图像)
      this.waterFallDom = document.createElement('canvas')
      this.waterFallDom.width = this.canvasSize
      this.waterFallDom.height = this.canvasRows
      this.waterFallCtx = this.waterFallDom.getContext('2d')
      // 创建图例
      this.legendDom = document.createElement('canvas')
      this.legendDom.width = 1
      this.legend = this.legendDom.getContext('2d')
    },
    // 绑定点击事件
    bindingClick(event) {
      this.coordinatePercentage = this.getCoordinatePercentage(event.offsetX, event.offsetY)
    },
    // 计算鼠标点击位置 x、y 百分比
    getCoordinatePercentage(x, y) {
      const result = []
      const width = this.dom.clientWidth - this.padding[1] - this.padding[3]
      const height = this.dom.clientHeight - this.padding[0] - this.padding[2]
      if (
        x > this.padding[3] &&
        x < this.dom.clientWidth - this.padding[1] &&
        y > this.padding[0] &&
        y < this.dom.clientHeight - this.padding[2]
      ) {
        result[0] = round((x - this.padding[3]) / width, 2)
        result[1] = round((y - this.padding[0]) / height, 2)
        return result
      } else {
        return null
      }
    },
    // 频谱颜色（内部基本不需要修改）
    setColormap() {
      this.colormap = colormap({
        colormap: this.colormapName,
        nshades: 150,
        format: 'rba',
        alpha: 1
      })
    },
    // 重新计算 canvas 宽度，使得页面在缩放实时重制
    setWidth() {
      // this.canvas.height = this.dom.clientHeight - this.padding[2]
      this.canvas.height = this.dom.clientHeight - this.padding[2]
      this.canvas.width = this.dom.clientWidth
    },
    // * 输入数据，触发绘制
    addData(data) {
      this.setWidth()
      if (data.length !== this.canvasSize) {
        this.canvasSize = data.length
        this.cutEnd = this.canvasSize
        this.waterFallCtx.canvas.width = data.length
      }
      this.addWaterfallRow(data)
      this.drawImage()
      this.drawLegend()
    },
    // 绘制瀑布图，隐藏状态
    addWaterfallRow(data) {
      // 将已生成的图像向下移动一个像素
      this.waterFallCtx.drawImage(this.waterFallCtx.canvas,
        0, 0, this.canvasSize, this.canvasRows - 1,
        0, 1, this.canvasSize, this.canvasRows - 1)
      // 通过数据绘制第一行图像，并插入在第一行像素中
      const imageData = this.rowToImageData(data)
      this.waterFallCtx.putImageData(imageData, 0, 0)
    },
    // 绘制单行图像
    rowToImageData(data) {
      const imageData = this.waterFallCtx.createImageData(data.length, 1)
      for (let i = 0; i < imageData.data.length; i += 4) {
        const cindex = this.squeeze(data[i / 4], 0, 135)
        const color = this.colormap[cindex]
        imageData.data[i + 0] = color[0]
        imageData.data[i + 1] = color[1]
        imageData.data[i + 2] = color[2]
        imageData.data[i + 3] = 255
      }
      return imageData
    },
    // 返回数据对应的 colormap
    squeeze(data, outMin, outMax) {
      if (data <= this.minDb) {
        return outMin
      } else if (data >= this.maxDb) {
        return outMax
      } else {
        return round((data - this.minDb) / (this.maxDb - this.minDb) * outMax)
      }
    },
    // 绘制到 canvas 中
    drawImage() {
      const width = this.ctx.canvas.width
      const height = this.ctx.canvas.height
      // const height = this.randomArray.length
      if (!this.waterFallCtx.canvas.width) return
      this.ctx.drawImage(this.waterFallCtx.canvas,
        this.cutStart, 0, this.cutEnd - this.cutStart, this.canvasRows,
        this.padding[3], this.spectrumHeight,
        width - this.padding[3] - this.padding[1], height - this.spectrumHeight)
    },
    // 绘制图例
    drawLegend() {
      const imageData = this.legend.createImageData(1, this.colormap.length)
      for (let i = 0; i < this.colormap.length; i++) {
        const color = this.colormap[i]
        imageData.data[imageData.data.length - i * 4 + 0] = color[0]
        imageData.data[imageData.data.length - i * 4 + 1] = color[1]
        imageData.data[imageData.data.length - i * 4 + 2] = color[2]
        imageData.data[imageData.data.length - i * 4 + 3] = 255
      }
      this.legend.putImageData(imageData, 0, 0)
      this.ctx.drawImage(this.legend.canvas,
        0, 0, 1, this.colormap.length,
        this.padding[3] * 3 / 4 - 5, 0,
        this.padding[3] / 4, this.ctx.canvas.height)
      this.ctx.font = '12px Arial'
      this.ctx.textAlign = 'end'
      this.ctx.fillStyle = '#fff'
      this.ctx.fillText(this.maxDb, this.padding[3] * 3 / 4 - 10, 12)
      this.ctx.fillText(this.minDb, this.padding[3] * 3 / 4 - 10, this.canvasRows - 6)
    },
    // 重新计算起始点和结束点，配合频谱图操作
    resize(payload) {
      this.cutStart = payload[0]
      this.cutEnd = payload[1]
      this.drawImage()
    },
    // 重制，显示完整图像
    reload() {
      this.cutStart = 0
      this.cutEnd = this.canvasSize
      this.drawImage()
    },
    clear() {
      this.waterFallCtx.clearRect(0, 0, this.waterFallDom.width, this.waterFallDom.height)
      this.ctx.clearRect(this.padding[3], this.padding[0], this.canvasSize - this.padding[1] - this.padding[3], this.canvasRows - this.padding[0] - this.padding[2])
    }
  },
  computed: {
    getHeight() {
      return `height: ${this.canvasRows}px;`
    }
  },
  watch: {}
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>
